# @Time    : 2018/12/31 20:39
# @Author  : heyin
import pandas as pd
import numpy as np
from pyecharts import Line, Scatter
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import VarianceThreshold
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.tree import export_graphviz
from sklearn.externals import joblib


def recommend(df):
    # print(df)
    # if (df.score >= 7 and df.country == '国内') or (df.score >= 7.5 and df.country == '国外'):
    #     return '推荐'
    # else:
    #     return '不推荐'

    if df.score >= 7:
        return '推荐'
    else:
        return '不推荐'


def add_recommend():
    """给数据添加是否推荐列:推荐，不推荐两种选择"""
    df = pd.read_csv('./data.csv')
    # df['recommend'] = 'B'
    # r_s = list()
    # for row in df.iterrows():
    #     if row[1]['score'] >= 7.5:
    #         r_s.append('A')
    #     elif (row[1]['score']) >= 5:
    #         r_s.append('B')
    #     else:
    #         r_s.append('C')
    # recommend = pd.DataFrame(r_s, columns=['recommend'])
    # df = pd.concat([df, recommend], axis=1)
    # df.to_csv('./new_data.csv', index=False)

    # 以下高效率处理
    # df['recommend_2'] = df.country.map(l)
    # df.to_csv('./new_data.csv', index=False)

    # 根据两列的条件判断后增加是否推荐度列
    df['recommend'] = df.apply(recommend, axis=1)
    # print(df['recommend_2'])
    df.to_csv('./new_data.csv', index=False)


def jueceshu():
    """利用决策树分类"""
    df = pd.read_csv('./new_data.csv')
    # df = df.sort_values(by=['vote_count'])  # 倒序排列
    # print(df.head(10))
    # 从数据中筛选出vote_count大于1000的电影
    df = df[df['vote_count'] > 2000]  # 过滤后还有15199部电影，56个特征

    # TODO  时长的过滤 大于等于80分钟
    # print(df[df['vote_count'] > 1000].shape)
    # 从数据中删除title，vote_count，score列
    # 不能通过评价人数来判断一个电影的优劣，因为新电影是没有评价人数的
    # df.drop(['title', 'vote_count', 'score'], axis=1, inplace=True)

    # 删掉后两个主演的信息后，存在nan的行数减少了大约九百行
    # df.drop(
    #     ['title', 'vote_count', 'score', 'actor4_best', 'actor5_best', 'actor4_worst', 'actor5_worst', 'actor4_recent',
    #      'actor5_recent','actor1_recent','actor2_recent','actor3_recent',
    #      'actor3_worst','actor2_worst','actor1_worst'], axis=1, inplace=True)

    df = df.loc[:, 'year':]

    # print(df.values)
    # print(df.values==df.values)  # 得到布尔值中的False就是nan
    # print(np.count_nonzero(df.values!=df.values))
    # df.dropna(axis=0, how='any', inplace=True)  # 只要有nan就删除整行记录，剩余shape(12711, 53)，五个主演
    df.dropna(axis=0, how='any', inplace=True)  # 只要有nan就删除整行记录，剩余shape(13669, 47)，三个主演

    print(df.shape)
    y = df['recommend_2']  # series，stars作为目标值
    x = df.drop(['year', 'stars', 'recommend_2', 'recommend'], axis=1)  # 删掉stars后其余的作为特征值

    # 决策树不需要对特征进行特别处理
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.25, random_state=1)
    for i in range(2, 20):
        # dtc = DecisionTreeClassifier(max_depth=7, min_samples_split=11, min_samples_leaf=7, random_state=2)  # i取大约789最佳
        dtc = DecisionTreeClassifier(max_depth=7, min_samples_split=14)  # i取大约789最佳
        dtc.fit(x_train, y_train)
        score = dtc.score(x_test, y_test)
        s = dtc.score(x_train, y_train)
        print('测试集准确率', i, score)
        print('训练集准确率', i, s)


        # (12711, 53)，五个主演
        # 测试集准确率
        # 0.6661422278162367
        # 训练集准确率
        # 0.7106891849365362


def knn():
    df = pd.read_csv('./new_data.csv')
    # 过滤出有时长且评价人数大于1000的电影
    df = df[df['vote_count'] > 1000]
    # df = df[df['runtime'] == df['runtime']]  # 取出时间不为nan的行
    df = df.loc[:, 'year':]
    df.dropna(axis=0, how='any', inplace=True)

    y = df['recommend']
    x = df.drop(['recommend', 'stars', 'country'], axis=1)

    # 决策树不需要对特征进行特别处理
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=3, random_state=1)
    # 利用knn计算需要进行标准化或者归一化处理，由于归一化受最大值最小值影响较大，因此采用标准化
    sd = StandardScaler()
    x_train = sd.fit_transform(x_train)
    x_test = sd.transform(x_test)
    for i in range(4, 11):
        knc = KNeighborsClassifier(n_neighbors=i)
        knc.fit(x_train, y_train)
        score = knc.score(x_test, y_test)
        s = knc.score(x_train, y_train)

        print('测试集准确率', i, score)
        print('训练集准确率', i, s)


def suijisenlin():
    df = pd.read_csv('./new_data.csv')
    # 过滤出有时长且评价人数大于1000的电影
    # df = df[df['vote_count'] > 1000]
    # df = df[df['runtime'] == df['runtime']]  # 取出时间不为nan的行
    df = df.loc[:, 'year':]
    df.dropna(axis=0, how='any', inplace=True)

    y = df['recommend']
    x = df.drop(['recommend', 'stars', 'country', 'runtime'], axis=1)
    # x = df.drop(
    #     ['recommend', 'stars', 'country', 'actor4_best', 'actor5_best', 'actor4_worst', 'actor5_worst', 'actor4_recent',
    #      'actor5_recent'], axis=1)
    print(x.shape)
    # vt = VarianceThreshold(threshold=0.1)
    # x = vt.fit_transform(x)
    # print(x.shape)
    # for i in range(1, 6):
    #     x['actor%s' % i] = x['actor%s_best' % i] * 0.3 + x['actor%s_worst' % i] * 0.3 + x['actor%s_recent' % i] * 0.4
    #     x = x.drop(['actor%s_best' % i, 'actor%s_worst' % i, 'actor%s_recent' % i], axis=1)

    # for i in range(10):
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)
    # print(x_test)
    # 使用随机森林和交叉验证求出最佳参数
    rfc = RandomForestClassifier()
    params = {'n_estimators': [300], 'max_depth': [25],
              'min_samples_split': [5], 'min_samples_leaf': [15]}
    gsc = GridSearchCV(rfc, params, cv=5)
    gsc.fit(x_train, y_train)
    joblib.dump(gsc, './suijisenlin.pkl')

    print('测试集效果score', gsc.score(x_test, y_test))
    print('训练集效果score', gsc.score(x_train, y_train))
    # print(gsc.best_estimator_)
    print(classification_report(y_test, y_pred=gsc.predict(x_test), labels=['推荐', '不推荐'], target_names=['推荐', '不推荐']))
    print('*' * 50)


def ceshi():
    model = joblib.load("./suijisenlin.pkl")
    c = [2019,8.16,6.54,6.4,5,5,4.68,5.44,6.4,5,5,5.96,5.58,6.4,5,5,8.23,7.17,7.37,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
    # print(np.array(c).reshape(1, len(c)))
    pred = model.predict(np.array(c).reshape(1, len(c)))
    print(pred)


def logic():
    """逻辑回归"""
    df = pd.read_csv('./new_data.csv')
    # 过滤出有时长且评价人数大于1000的电影
    df = df[df['vote_count'] > 1000]
    print(df.shape)
    df = df[df['runtime'] == df['runtime']]  # 取出时间不为nan的行
    df = df.loc[:, 'year':]

    # x = df.drop(['stars', 'recommend', 'country'], axis=1)  # 删掉stars后其余的作为特征值
    df = df.loc[:, ['year', 'actor1_best', 'actor2_best', 'actor3_best',
                    'actor1_worst', 'actor2_worst',
                    'actor3_worst', 'actor1_recent',
                    'actor2_recent', 'actor3_recent',
                    'd_best', 'd_worst', 'd_recent', 'runtime', 'Reality-TV',
                    'News', '战争', '悬疑', '武侠', '鬼怪', '古装', '儿童', '运动', '动画', '科幻', '喜剧',
                    '奇幻', '黑色电影', '剧情', '传记', '灾难', 'Adult', '犯罪', '歌舞', '悬念', '纪录片', '家庭',
                    '情色', '爱情', '恐怖', '西部', '历史', '动作', '同性', '惊悚', '荒诞', '短片', '惊栗', '冒险',
                    '音乐', 'recommend']]  # 手动选择特征
    df.dropna(axis=0, how='any', inplace=True)
    print(df.shape)
    y = df['recommend']
    x = df.drop('recommend', axis=1)

    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=1)
    # 利用knn计算需要进行标准化或者归一化处理，由于归一化受最大值最小值影响较大，因此采用标准化
    sd = StandardScaler()
    x_train = sd.fit_transform(x_train)
    x_test = sd.transform(x_test)

    c = [0.01, 0.1, 1.0, 10.0, 100.0]
    # for i in range(5):
    #     print('*' * 20)
    lr = LogisticRegression(C=10, solver='lbfgs', penalty='l2',
                            multi_class='ovr')  # 多分类指定multi_class='ovr' ovr: one vs rest
    lr.fit(x_train, y_train)
    y_pred = lr.predict(x_test)
    print("训练集score:", lr.score(x_train, y_train))
    print("测试集score:", lr.score(x_test, y_test))
    print(classification_report(y_test, y_pred, labels=['推荐', '不推荐'], target_names=['推荐', '不推荐']))


if __name__ == '__main__':
    # jueceshu()
    # knn()
    #suijisenlin()
    ceshi()
    # logic()

    # add_recommend()
